#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/sysfs.h>
#include <linux/string.h>
#include "my_led_dt.h"
static struct my_led_dev* my_led = NULL;
// ioread32和iowrite32为内核提供的函数，用于读写IO内存，内部插入了内存屏障
// ioread32的内存屏障插在读之后，确保读完之后才能执行后面的代码
// iowrite32的内存屏障插在写之前，确保写之前的代码执行完毕
// ioread32和iowrite32内部将指针由u32*强制转换为volatile u32*,保证不被编译器优化
// ioread32和iowrite32内部调用的是readl和writel宏，该宏不检查类型，不建议直接使用
static void led_open(struct my_led_dev* dev ,struct led_ctl_pin* pin, 
	unsigned int clk_mask, unsigned int mux_mode, unsigned int drir_mask)
{
	unsigned int val = 0;

	mutex_lock(&dev->mutex);
	val = ioread32(pin->clk_pin);
	val |= clk_mask; 	 	// 开启GPIO时钟
	iowrite32(val, pin->clk_pin);

	val = ioread32(pin->mux_pin);
	val &= ~0xF;            // 将复用模式选择的低4位清零
	val |= mux_mode; 		// 配制引脚复用为GPIO
	iowrite32(val, pin->mux_pin);

	val = ioread32(pin->drir_pin);
	val |= drir_mask;  	    // 配置引脚为输出，1为输出，0为输入
	iowrite32(val, pin->drir_pin);
	mutex_unlock(&dev->mutex);
}
// 控制led灯点亮和熄灭，如果led灯由熄灭到点亮则记录点亮的次数
// 若led灯控制前状态和控制后的状态一致，则不做操作
static void led_ctl(struct my_led_dev* dev, void __iomem *addr, 
				int mask, int val)
{
	unsigned int reg = 0;
	mutex_lock(&dev->mutex);
	reg = ioread32(addr);
	if (val) {  // 非0灯亮
		if (mask == (reg & mask)) {      // led灯处于熄灭状态，则需要点亮led灯   
			reg &= ~mask;               // 输出低电平，点亮led 
			iowrite32(reg, addr);
			if ((dev->r_p.pin.dr_pin) == addr)
				dev->r_on_cnt++;
			if ((dev->g_p.pin.dr_pin) == addr)
				dev->g_on_cnt++;
			if ((dev->b_p.pin.dr_pin) == addr)
				dev->b_on_cnt++;
			goto unlock;
		}
	}
	else { // 0灯灭
		if (0 == (reg & mask)) {     // led灯处于点亮状态，则需要熄灭led灯
			reg |= mask;         // 输出高电平，熄灭led
			iowrite32(reg, addr);
		}
	}
unlock:
	mutex_unlock(&dev->mutex);
}

static int my_led_open(struct inode* inode, struct file* filp)
{
	int on = 1, off = 0;
	struct my_led_dev* dev = container_of(inode->i_cdev, struct my_led_dev, cdev); 

	led_open(dev, &dev->r_p.pin, GPIO1_4_CLK_MASK, GPIO1_4_MUX_MODE, 
					GPIO1_4_MASK);
	led_open(dev, &dev->g_p.pin, GPIO4_20_CLK_MASK, GPIO4_20_MUX_MODE, 
					GPIO4_20_MASK);
	led_open(dev, &dev->b_p.pin, GPIO4_19_CLK_MASK, GPIO4_19_MUX_MODE, 
					GPIO4_19_MASK);
	// 根据设备树中status属性，配置led打开打状态，
	if (0 == strcmp(dev->r_p.status, "okay"))
		led_ctl(dev, dev->r_p.pin.dr_pin, GPIO1_4_MASK, on); // 灯亮
	else 
		led_ctl(dev, dev->r_p.pin.dr_pin, GPIO1_4_MASK, off);// 灯灭

	if (0 == (strcmp(dev->g_p.status, "okay")))
		led_ctl(dev, dev->g_p.pin.dr_pin, GPIO4_20_MASK, on);
	else 
		led_ctl(dev, dev->g_p.pin.dr_pin, GPIO4_20_MASK, off);

	if (0 == (strcmp(dev->b_p.status, "okay")))
		led_ctl(dev, dev->b_p.pin.dr_pin, GPIO4_19_MASK, on);
	else
		led_ctl(dev, dev->b_p.pin.dr_pin, GPIO4_19_MASK, off);
	filp->private_data = dev;
	return 0;
}

static ssize_t my_led_write(struct file* filp, const char __user* buf, 
				size_t size, loff_t* ppos)
{
	struct my_led_dev* dev = filp->private_data;
	int ret, val[3];
	if (size != sizeof(val)) return -EINVAL; 
	// 成功返回0,失败返回失败的数目
	ret = copy_from_user(&val, buf, sizeof(val)); 
	if (0 != ret) return -EFAULT;
	// 非0-灯亮，0-灯灭，GPIO引脚输出低电平灯亮，输出高点平灯灭
	led_ctl(dev, dev->r_p.pin.dr_pin, GPIO1_4_MASK, val[0]);
	led_ctl(dev, dev->g_p.pin.dr_pin, GPIO4_20_MASK, val[1]);
	led_ctl(dev, dev->b_p.pin.dr_pin, GPIO4_19_MASK, val[2]);
	return sizeof(val);
}
// 文件操作函数结构体
static const struct file_operations my_led_fops = {
	.owner = THIS_MODULE,
	.write = my_led_write,
	.open = my_led_open,
};

/******************************初始化和注册cdev结构体******************************/
static int set_up_my_led_cdev(struct my_led_dev* dev, int cnt)
{
	int err;
	cdev_init(&dev->cdev, &my_led_fops);
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev, dev->devno, cnt); // 出错返回负值
	if (err < 0)
		printk(KERN_ERR "adding my_led cdev %d error, errno %d\n", cnt, err);
	return err;
}
/*******************************定义设备的属性********************************/
static ssize_t red_show(struct device* dev, struct device_attribute* attr,char* buf)
{
	return sprintf(buf, "red led lighting times %u\n", my_led->r_on_cnt);
} // echo 非0，灯亮，echo 0,灯灭
static ssize_t red_store(struct device *dev, struct device_attribute *attr,  
                				const char *buf, size_t count)
{	int err;
	unsigned int val;
	// 将echo输入得字符串转换为无符号整数，10表示十进制
	err = kstrtouint(buf, 10, &val); 
	if (err)
		return err;
	led_ctl(my_led, my_led->r_p.pin.dr_pin, GPIO1_4_MASK, val);
	return count;
} 
static ssize_t green_show(struct device *dev, struct device_attribute *attr,char *buf)
{
	return sprintf(buf, "green led lighting times %u\n", my_led->g_on_cnt);
}
static ssize_t green_store(struct device *dev, struct device_attribute *attr,  
                				const char *buf, size_t count)
{	int err;
	unsigned int val;
	err = kstrtouint(buf, 10, &val);
	if (err)
		return err;
	led_ctl(my_led, my_led->g_p.pin.dr_pin, GPIO4_20_MASK, val);
	return count;
} 
static ssize_t blue_show(struct device *dev, struct device_attribute *attr,char *buf)
{
	return sprintf(buf, "blue led lighting times %u\n", my_led->b_on_cnt);
}
static ssize_t blue_store(struct device *dev, struct device_attribute *attr,  
                				const char *buf, size_t count)
{	int err;
	unsigned int val; 
	err = kstrtouint(buf, 10, &val);
	if (err)
		return err;
	led_ctl(my_led, my_led->b_p.pin.dr_pin, GPIO4_19_MASK, val);
	return count;
} 
// 红色led设备属性，生成的属性结构体名称为dev_attr_red，类型为struct device_attribute，
// mode为0644，所属者可以读写，其他只能读
static DEVICE_ATTR(red, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, red_show, red_store);
// 绿色led设备属性，生成的属性结构体名称为dev_attr_green，类型为struct device_attribute，
// mode为0644，所属者可以读写，其他只能读
static DEVICE_ATTR(green, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, green_show, green_store);
// 蓝色led设备属性，生成的属性结构体名称为dev_attr_blue，类型为struct device_attribute，
// mode为0644，所属者可以读写，其他只能读
static DEVICE_ATTR(blue, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, blue_show, blue_store);
static struct attribute* led_colour_attrs[] = {
	&dev_attr_red.attr,
	&dev_attr_green.attr,
	&dev_attr_blue.attr,
	NULL,
};
/* 创建led颜色属性组，生成的属性结构体名称为led_colour_group，类型为static const 
   struct attribute_group，使用led_colour_attrs初始化
   ATTRIBUTE_GROUPS(led_colour); */
// 使用ATTRIBUTE_GROUPS宏创建属性组时编译会出现警告，这里直接定义led颜色属性组
static struct attribute_group led_colour_group = {
    .attrs = led_colour_attrs,
};
/************************映射led控制寄存器*************************/
static int led_ioremap(struct my_led_dev* dev)
{
	int ret = 0;
	dev->r_p.pin.dr_pin = ioremap(dev->r_p.reg[0], dev->r_p.reg[1]);
	dev->r_p.pin.drir_pin = dev->r_p.pin.dr_pin + 1;
	dev->r_p.pin.mux_pin = ioremap(dev->r_p.reg[2], dev->r_p.reg[3]);
	dev->r_p.pin.clk_pin = ioremap(dev->r_p.reg[4], dev->r_p.reg[5]);
	if ((NULL == dev->r_p.pin.dr_pin) || (NULL == dev->r_p.pin.mux_pin) || 
		(NULL == dev->r_p.pin.clk_pin)) {
		printk(KERN_ERR "red led reg ioremap error\n");
		ret = -ENOMEM;
		goto r_unmap;
	}
	// 蓝色led和绿色led引脚都属于GPIO4，只需映射一次
	dev->g_p.pin.dr_pin = ioremap(dev->g_p.reg[0], dev->g_p.reg[1]);
	dev->g_p.pin.drir_pin = dev->g_p.pin.dr_pin + 1;
	dev->g_p.pin.mux_pin = ioremap(dev->g_p.reg[2], dev->g_p.reg[3]);
	dev->g_p.pin.clk_pin = ioremap(dev->g_p.reg[4], dev->g_p.reg[5]);
	if ((NULL == dev->g_p.pin.dr_pin) || (NULL == dev->g_p.pin.mux_pin) || 
		(NULL == dev->g_p.pin.clk_pin)) {
		printk(KERN_ERR "green led reg ioremap error\n");
		ret = -ENOMEM;
		goto g_unmap;
	}
	// 蓝色led和绿色led引脚都属于GPIO4，只需映射一次
	dev->b_p.pin.dr_pin = dev->g_p.pin.dr_pin;
	dev->b_p.pin.drir_pin = dev->g_p.pin.drir_pin;
	dev->b_p.pin.mux_pin = ioremap(dev->b_p.reg[2], dev->b_p.reg[3]);
	dev->b_p.pin.clk_pin = dev->g_p.pin.clk_pin;
	if ((NULL == dev->b_p.pin.mux_pin)) {
		printk(KERN_ERR "blue led reg ioremap error\n");
		ret = -ENOMEM;
		goto g_unmap;
	}
	return ret;
g_unmap:
	if (NULL != dev->g_p.pin.dr_pin)
		iounmap(dev->g_p.pin.dr_pin);
	if (NULL != dev->g_p.pin.mux_pin)
		iounmap(dev->g_p.pin.mux_pin);
	if (NULL != dev->g_p.pin.clk_pin)
		iounmap(dev->g_p.pin.clk_pin);
r_unmap:
	if (NULL != dev->r_p.pin.dr_pin)
		iounmap(dev->r_p.pin.dr_pin);
	if (NULL != dev->r_p.pin.mux_pin)
		iounmap(dev->r_p.pin.mux_pin);
	if (NULL != dev->r_p.pin.clk_pin)
		iounmap(dev->r_p.pin.clk_pin);
	return ret;
}
/************************从设备树中读取节点属性*************************/
static int read_property(struct led_property* led_p, char* path)
{
	int ret;
	struct device_node* n;
	struct property* p;

	// 从设备树中获取设备节点
	n = of_find_node_by_path(path);
	if (NULL == n) {
		printk(KERN_ERR "find led node error by %s\n", path);
		return -EINVAL;
	}
	led_p->node = n;

	// 从设备树中获取兼容性属性
	p = of_find_property(led_p->node, "compatible", NULL);
	if (NULL == p) {
		printk(KERN_ERR "find led node compatible property error\n");
		return -EINVAL;
	}
	led_p->proper = p;

	// 从设备树中获取status属性
	ret = of_property_read_string(led_p->node, "status", &led_p->status);
	if (ret < 0) {
		printk(KERN_ERR "find led node status property error\n");
		return -EINVAL;
	}

	// 从设备树中获取reg属性
	ret = of_property_read_u32_array(led_p->node, "reg", led_p->reg, 6);
	if (ret < 0) {
		printk(KERN_ERR "find led node reg property error\n");
		return -EINVAL;
	}
	return 0;
}

static void print_property(struct led_property* led_p)
{
	int i;
	printk(KERN_INFO "node name: %s\n", led_p->node->name);
	printk(KERN_INFO "compatible: %s\n", (char*)(led_p->proper->value));
	printk(KERN_INFO "status: %s\n", led_p->status);
	for (i = 0; i < 6; i += 2)
		printk(KERN_INFO "%#x  %#x\n", led_p->reg[i], led_p->reg[i+1]);
	printk(KERN_INFO "\n");	
}
/****************从设备树中提取信息，并将led控制寄存器地址进行映射*****************/
static int get_led_resource_and_map(struct my_led_dev* dev)
{
	int ret = 0;

	// 从设备树中提取led属性
	ret = read_property(&dev->r_p, "/rgb_led/r_led");
	if (ret < 0) return ret;
	print_property(&dev->r_p);
	ret = read_property(&dev->g_p, "/rgb_led/g_led");
	if (ret < 0) return ret;
	print_property(&dev->g_p);
	ret = read_property(&dev->b_p, "/rgb_led/b_led");
	if (ret < 0) return ret;
	print_property(&dev->b_p);

	ret = led_ioremap(dev);
	if (ret < 0) return ret;
	return ret;
}
/****************************模块初始化************************************/
static int __init my_led_init(void)
{
	int ret = 0;
	dev_t devno = 0;
	// 动态分配设备号，传入的devno参数为0,使用unregister_chrdev_region注销动态分配的设备号
	ret = alloc_chrdev_region(&devno, 0, 1, "my_led");
	if (ret < 0) {
		printk(KERN_ERR "alloc_chrdev_region() failed %d\n", ret);
		return ret;	
	}
	// 分配设备结构体内存并将分配的内存清0
	my_led = kzalloc(sizeof(struct my_led_dev), GFP_KERNEL);
	if (NULL == my_led) {
		ret = -ENOMEM;
		printk(KERN_ERR "kzalloc() failed %d\n", ret);
		goto unreg_chrdev;
	}
	my_led->devno = devno;

	// 从设备树获取资源
	ret = get_led_resource_and_map(my_led);
	if (0 != ret) goto free_dev;

	ret = set_up_my_led_cdev(my_led, 1);
	if (ret < 0) goto free_dev;
	// 创建类和设备，当模块加载后会自动在/dev目录下生成设备节点
	my_led->my_led_class = class_create(THIS_MODULE, "my_led_class");
	if (IS_ERR(my_led->my_led_class)) {
		ret = PTR_ERR(my_led->my_led_class);
		printk(KERN_ERR "class_create() failed %d\n", ret);
		goto del_cdev;
	}
	my_led->my_led_device = device_create(my_led->my_led_class, NULL, 
								devno, NULL, "my_led");
	if (IS_ERR(my_led->my_led_device)) {
		ret = PTR_ERR(my_led->my_led_device);
		printk(KERN_ERR "device_create() failed %d\n", ret);
		goto clean_class;
	}
	// 使用sysfs_create_group可以创建一组属性文件，sysfs_remove_group移除一组属性文件
	// 使用sysfs_create_groups可以创建多组属性文件，ysfs_remove_groups移除多组属性文件
	ret = sysfs_create_group(&my_led->my_led_device->kobj, &led_colour_group);
	if(ret != 0) goto clean_device;

	// 初始化互斥体
	mutex_init(&my_led->mutex);
	printk(KERN_INFO "my_led module init OK, major %u, minor %u\n", 
			MAJOR(devno), MINOR(devno));
	return 0;
clean_device:
	device_destroy(my_led->my_led_class, devno);
clean_class: 
	class_destroy(my_led->my_led_class);
del_cdev:
	cdev_del(&my_led->cdev);
free_dev:
	kfree(my_led);
	my_led = NULL;
unreg_chrdev:
	unregister_chrdev_region(devno, 1);
    return ret;
}
/********************注销已映射的三个led灯控制寄存器************************/
static void r_iounmap(struct my_led_dev* dev)
{
	iounmap(dev->r_p.pin.dr_pin);
	iounmap(dev->r_p.pin.mux_pin);
	iounmap(dev->r_p.pin.clk_pin);
}
static void g_iounmap(struct my_led_dev* dev)
{
	iounmap(dev->g_p.pin.dr_pin);
	iounmap(dev->g_p.pin.mux_pin);
	iounmap(dev->g_p.pin.clk_pin);	
}
static void b_iounmap(struct my_led_dev* dev)
{
	iounmap(dev->b_p.pin.mux_pin);	
}
/********************模块注销************************/
static void __exit my_led_exit(void)
{
	sysfs_remove_group(&my_led->my_led_device->kobj, &led_colour_group);
	device_destroy(my_led->my_led_class, my_led->devno);
	class_destroy(my_led->my_led_class);
	cdev_del(&my_led->cdev);
	b_iounmap(my_led);
	g_iounmap(my_led);
	r_iounmap(my_led);
	unregister_chrdev_region(my_led->devno, 1);
	kfree(my_led);
	my_led = NULL;
	printk(KERN_INFO "my_led module exit\n");
}

module_init(my_led_init);
module_exit(my_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("liyang.plus@foxmail.com");
MODULE_VERSION("v1.00");